﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace VisualBounds.Imaging.PaintBucket.UI
{
    public partial class ImageControl : UserControl
    {
        #region Declaration

        private Point MousePositionOld;
        private int width, height, x, y;
        public Rectangle ImageClipping;

        private MouseButtons MouseDown = MouseButtons.None;
        private Point MouseLocation = new Point(0,0);

        private Image _image;
        /// <summary>
        /// Gets or sets the image that is displayed by VisualBounds.Imaging.PaintBucket.UI.ImageControl.
        /// </summary>
        [Browsable(true), Description("The image displayed in the ImageControl.")]
        public Image Image
        {
            get
            {
                return _image;
            }
            set
            {
                _image = value;
                Invalidate();
            }
        }

        private int _scaleFactor = 1;
        /// <summary>
        /// Gets or sets the scaling factor between the normal Image and the displayed Image.
        /// </summary>
        [Browsable(true), Description("Specifies the scaling factor between the normal Image and the displayed Image.")]
        public int ScaleFactor
        {
            get
            {
                return _scaleFactor;
            }
            set
            {
                _scaleFactor = value;
                Invalidate();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether a dropshadow should be rendered below the image.
        /// </summary>
        [Browsable(true), Description("Indicates whether a dropshadow should be rendered below the image.")]
        public bool DropShadow { get; set; }

        /// <summary>
        /// Gets or sets a value indicating where and how large the selection rendering should be.
        /// </summary>
        [Browsable(true), Description("Indicates where and how large the selection rendering should be.")]
        public Rectangle Selection { get; set;}

        /// <summary>
        /// Gets or sets a value indicating whether the selection box can be edited.
        /// </summary>
        [Browsable(true), Description("Indicates whether the selection box can be edited.")]
        public bool SelectionEditable { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the debug mode is enabled or not.
        /// </summary>
        [Browsable(true), Description("Indicates whether the debug mode is enabled or not.")]
        public bool Debug { get; set; }

        /// <summary>
        /// Gets or sets the shortcut menu to display when the user right-clicks the background.
        /// </summary>
        [Browsable(true), Category("Behavior"), Description("The shortcut menu to display when the user right-clicks the background.")]
        public ContextMenuStrip backgroundContextMenuStrip { get; set; }
        /// <summary>
        /// Gets or sets the shortcut menu to display when the user right-clicks the selection.
        /// </summary>
        [Browsable(true), Category("Behavior"), Description("The shortcut menu to display when the user right-clicks the selection.")]
        public ContextMenuStrip selectionContextMenuStrip { get; set; }
        /// <summary>
        /// Gets or sets the shortcut menu to display when the user right-clicks the image.
        /// </summary>
        [Browsable(true), Category("Behavior"), Description("The shortcut menu to display when the user right-clicks the image.")]
        public ContextMenuStrip imageContextMenuStrip { get; set; }

        #region Hidden

        [Browsable(false)]
        public override bool AutoScroll
        {
            get
            {
                return base.AutoScroll;
            }
            set
            {
                base.AutoScroll = true;
            }
        }

        [Browsable(false)]
        public override bool AutoSize
        {
            get
            {
                return base.AutoSize;
            }
            set
            {
                base.AutoSize = false;
            }
        }

        [Browsable(false)]
        public override Point AutoScrollOffset
        {
            get
            {
                return base.AutoScrollOffset;
            }
            set
            {
                base.AutoScrollOffset = value;
            }
        }

        #endregion

        #endregion


        public ImageControl()
        {
            InitializeComponent();
            this.AutoScroll = true;
            this.DoubleBuffered = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.Image == null)
                return;

            width = Image.Width * ScaleFactor;
            height = Image.Height * ScaleFactor;
            Size size = new Size(width, height);

            this.AutoScrollMinSize = size;

            if (width < this.Width)
                x = (this.Width - width) / 2;
            else
            {
                x = this.AutoScrollPosition.X;
            }
            if (height < this.Height)
                y = (this.Height - height) / 2;
            else
            {
                y = this.AutoScrollPosition.Y;
            }

            if (this.DropShadow)
            {
                LinearGradientBrush brush = new LinearGradientBrush(new Point(x, y + height), new Point(x, y + height + 4), Color.LightGray, Color.Transparent);
                e.Graphics.FillRectangle(brush, new Rectangle(x + 1, y + height - 1, width, 4));
                brush = new LinearGradientBrush(new Point(x + width, y), new Point(x + width + 4, y), Color.LightGray, Color.Transparent);
                e.Graphics.FillRectangle(brush, new Rectangle(x + width, y + 1, 4, height));
            }

            e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
            e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            e.Graphics.DrawImage(Image, x, y, width, height);

            if (ScaleFactor >= 4)
            {
                for (int i = ScaleFactor; i < width; i += ScaleFactor)
                {
                    e.Graphics.DrawLine(new Pen(Color.FromArgb(150, Color.Gray)), i + x, y, i + x, height + y);
                }
                for (int i = ScaleFactor; i < height; i += ScaleFactor)
                {
                    e.Graphics.DrawLine(new Pen(Color.FromArgb(150, Color.Gray)), x, i + y, width + x, i + y);
                }
            }

            Rectangle SelectionM = Selection;

            SelectionM.X = (SelectionM.X * ScaleFactor) + x;
            SelectionM.Y = (SelectionM.Y * ScaleFactor) + y;
            SelectionM.Width = SelectionM.Width * ScaleFactor;
            SelectionM.Height = SelectionM.Height * ScaleFactor;

            e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(112, Color.CornflowerBlue)), SelectionM);
            e.Graphics.DrawRectangle(new Pen(Color.FromArgb(255, Color.CornflowerBlue)), SelectionM);

            if (SelectionEditable)
            {
                e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

                e.Graphics.FillRectangle(Brushes.Black, SelectionM.X - 3, SelectionM.Y - 3, 6, 6);
                e.Graphics.FillRectangle(Brushes.Black, SelectionM.X + SelectionM.Width - 3, SelectionM.Y - 3, 6, 6);
                e.Graphics.FillRectangle(Brushes.Black, SelectionM.X - 3, SelectionM.Y + SelectionM.Height - 3, 6, 6);
                e.Graphics.FillRectangle(Brushes.Black, SelectionM.X + SelectionM.Width - 3, SelectionM.Y + SelectionM.Height - 3, 6, 6);

                e.Graphics.FillRectangle(Brushes.White, SelectionM.X - 2, SelectionM.Y - 2, 4, 4);
                e.Graphics.FillRectangle(Brushes.White, SelectionM.X + SelectionM.Width - 2, SelectionM.Y - 2, 4, 4);
                e.Graphics.FillRectangle(Brushes.White, SelectionM.X - 2, SelectionM.Y + SelectionM.Height - 2, 4, 4);
                e.Graphics.FillRectangle(Brushes.White, SelectionM.X + SelectionM.Width - 2, SelectionM.Y + SelectionM.Height - 2, 4, 4);

            }

            e.Graphics.PixelOffsetMode = PixelOffsetMode.Default;
            e.Graphics.InterpolationMode = InterpolationMode.Default;

            e.Graphics.DrawRectangle(Pens.DarkGray, x, y, width, height);

            if (this.HorizontalScroll.Visible)
                width = this.Width;
            if (this.VerticalScroll.Visible)
                height = this.Height;

            x = x - this.AutoScrollPosition.X;
            y = y - this.AutoScrollPosition.Y;
            ImageClipping = new Rectangle(x, y, width, height);


            if (Debug)
            {
                e.Graphics.DrawString("Location: " + MouseLocation.X.ToString() + "," + MouseLocation.Y.ToString() + " (" + (PointToClient(MousePosition).X - x).ToString() + ", " + (PointToClient(MousePosition).Y - y).ToString() + ")", SystemFonts.DefaultFont, Brushes.Black, 1, 1);
                e.Graphics.DrawString("Button Down: " + (MouseDown == MouseButtons.Left ? "Left" : MouseDown == MouseButtons.Right ? "Right" : MouseDown == System.Windows.Forms.MouseButtons.Middle ? "Middle" : "None"), SystemFonts.DefaultFont, Brushes.Black, 1, 12);
            }

        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            Invalidate();
        }

        protected override void OnScroll(ScrollEventArgs se)
        {
            base.OnScroll(se);
            Invalidate();
        }

        protected override void OnMouseCaptureChanged(EventArgs e)
        {
            if(this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseCaptureChanged(e);
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            if (this.ImageClipping.Contains(PointToClient(MousePosition)))
                base.OnMouseEnter(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            e = GenerateArgs(e);
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseMove(e);

            if (!this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePositionOld))
                    base.OnMouseLeave(new EventArgs());

            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                if (!this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePositionOld))
                    base.OnMouseEnter(new EventArgs());

            if (SelectionEditable)
            {

                Point point = PointToClient(MousePosition);

                if (MouseDown == MouseButtons.Left)
                {
                    int w = ((point.X - x) - Selection.X) / ScaleFactor;
                    int h = ((point.Y - y) - Selection.Y) / ScaleFactor;

                    if (w < (1 * ScaleFactor))
                        w = 1 * ScaleFactor + 5;
                    if (h < (1 * ScaleFactor))
                        h = 1 * ScaleFactor + 5;

                    Selection = new Rectangle(Selection.X, Selection.Y, w - 5, h - 5);
                }
            }
            MouseLocation = e.Location;
            MousePositionOld = MousePosition;
            if (Debug || MouseButtons == MouseButtons.Left)
                Invalidate();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseWheel(GenerateArgs(e));
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseClick(GenerateArgs(e));

            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (this.RectangleToScreen(new Rectangle((Selection.X * ScaleFactor) + x, (Selection.Y * ScaleFactor) + y, Selection.Width * ScaleFactor, Selection.Height * ScaleFactor)).Contains(MousePosition) && selectionContextMenuStrip != null)
                    selectionContextMenuStrip.Show(MousePosition);
                else if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition) && imageContextMenuStrip != null)
                    imageContextMenuStrip.Show(MousePosition);
                else if (backgroundContextMenuStrip != null)
                    backgroundContextMenuStrip.Show(MousePosition);

            }
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseDoubleClick(GenerateArgs(e));
        }
    
        protected override void OnMouseHover(EventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
                base.OnMouseHover(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            if (!this.RectangleToScreen(new Rectangle(x + 1, y + 1, width - 2, height - 2)).Contains(MousePosition))
                base.OnMouseLeave(e);

            MouseDown = MouseButtons.None;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
            {
                base.OnMouseDown(GenerateArgs(e));

                Rectangle SelectionM = new Rectangle((Selection.X * ScaleFactor) + x, (Selection.Y * ScaleFactor) + y, Selection.Width * ScaleFactor, Selection.Height * ScaleFactor);
                if (this.RectangleToScreen(new Rectangle(SelectionM.X + SelectionM.Width - 3, SelectionM.Y + SelectionM.Height - 3, 6, 6)).Contains(MousePosition) ||
                    this.RectangleToScreen(new Rectangle(SelectionM.X - 3, SelectionM.Y - 3, 6, 6)).Contains(MousePosition))
                    MouseDown = e.Button;
            }
            if (Debug)
                Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (this.RectangleToScreen(new Rectangle(x, y, width, height)).Contains(MousePosition))
            {
                base.OnMouseUp(GenerateArgs(e));
                MouseDown = MouseButtons.None;
            }
            if (Debug)
                Invalidate();
        }


        private MouseEventArgs GenerateArgs(MouseEventArgs e)
        {
            int mx = (e.X - x) / ScaleFactor;
            int my = (e.Y - y) / ScaleFactor;
 
            return new MouseEventArgs(e.Button, e.Clicks, mx, my, e.Delta);
        }

    }
}